#include "mesh.h"
#include <fstream>
#include <iostream>
#include <sstream>

Mesh::Mesh() : GraphicElement(1)
{
    InitBuffers();
    InitBooleans();
    vbo->InitBuffers();
}


void Mesh::InitBooleans()
{
    normalActivated=0;
    colorActivated=0;
    picking=0;
}

void Mesh::Display()
{
    glPushMatrix(); //	On utilise PushMatrix afin que toutes les opérations sur la matrice de visualisation restent locales
    {
        if(lightOn)
        {
           UseMaterials();
        }
        Transformations();
        RenderBuffers(0);
    }
    glPopMatrix();
}

void Mesh::RenderBuffers(int Apicking)
{

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glEnableClientState(GL_VERTEX_ARRAY);

    if(Apicking)
    {
        glBindBuffer(GL_ARRAY_BUFFER,pickingBuffer);
        glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);
        glEnableClientState(GL_COLOR_ARRAY);
    }
    else
    {
        if(colorActivated)
        {
            glBindBuffer(GL_ARRAY_BUFFER,colorBuffer);
            glColorPointer(3, GL_FLOAT, 0, 0);
            glEnableClientState(GL_COLOR_ARRAY);
        }
    }
    if(!Apicking)
    {
        if(normalActivated)
        {
            glBindBuffer(GL_ARRAY_BUFFER,normalBuffer);
            glNormalPointer(GL_FLOAT,0, 0);
            glEnableClientState(GL_NORMAL_ARRAY);
        }

    }

    glDrawArrays(GL_TRIANGLES,0, size);

    /* desactivation des tableaux de sommet */

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);


    glFlush();
}

void Mesh::Picking()
{
    glPushMatrix(); // On utilise PushMatrix afin que toutes les opérations sur la matrice de visualisation restent locales
    {
        glDisable(GL_LIGHTING);
        Transformations();
        RenderBuffers(1);
        glEnable(GL_LIGHTING);
    }
    glPopMatrix();
}

void Mesh::FillBuffer()
{}

int Mesh::LoadMesh(std::string fileName)
{

    std::ifstream file(fileName.c_str(),std::ios::in);  /*!<    Ouverture du fichier en lecture */


    std::vector<std::string> lineContent;   /*!< Après le découpage du fichier, tout les "blocs" d'informations sont stoqué ici */
    std::string temp;   /*!< chaine de caractère utilisé uniquement pour stoquer temporaire un bloc de texte avant de
                        /*  l'inséré dans le tableau    */

    std::vector<float>          svertices;      /*!< Enregistre les informations concernant les sommets des mesh */
    std::vector<unsigned int>   indices;        /*!< Enregistre les indices des faces */
    std::vector<unsigned int>   coorTextures;   /*!< Enregistre les coordonnées des textures */
    std::vector<float>          normals;        /*!< Enregistre les normales des sommets */
    std::vector<unsigned int>   normalIndice;   /*!< Enregistre les indices des normales */



    while(file>>temp)   /*!<  lit le fichier jusqu'à atteindre un délimiteur (espace, retour à la ligne)    */
    {
        lineContent.push_back(temp);
    }


    for(int i=0;i<lineContent.size();i++)   /*!<    Je parcours toutes les chaines que j'ai extraites du fichier    */
    {
        if(lineContent[i]=="vn")   /*!<    Préfixe indiquant que les prochaines informations seront les normales des   */
        {                          /*!<    sommets                                                                    */
            for(int j=1;j<4;j++)
            {
                std::stringstream ss;
                float f;
                ss.str(lineContent[i+j]);
                ss >> f;

                normals.push_back(f);
            }
            i+=3;
        }
        else if( (lineContent[i])=="v")   /*!<    préfixe indiquant que les 3 prochains éléments extrait sont les coordonnées */
        {                               /*!<    des sommets                                                                 */
            for(int j=1;j<4;j++)
            {
                std::stringstream ss;
                float f;
                ss.str(lineContent[i+j]);
                ss >> f;

                svertices.push_back(f);
            }
            i+=3;
        }
        else if((lineContent[i])=="f")   /*!<    Préfixe indiquant que les prochaines informations seront les indices des */
        {                                   /*!<    sommets constituant la face     */

            int pos;
            if(pos=lineContent[i+1].find("/"))
            {
                for(int j=1;j<4;j++)
                {
                    pos=lineContent[i+j].find("/");
                    std::string vertexIndice; std::string textureIndice; std::string normalIndicestr;

                    vertexIndice=lineContent[i+j].substr(0,pos);

                    if(!lineContent[i+j][pos+1]=='/')
                    {
                        textureIndice=lineContent[i+j].substr(pos,lineContent[i+j].find("/",pos));
                        normalIndicestr=lineContent[i+j].substr(lineContent[i+j].find("/",pos),temp.size());

                        std::stringstream * ss= new std::stringstream();
                        unsigned int f;
                        ss->str(vertexIndice);
                        *ss>>f;
                        indices.push_back(f);
                        ss =  new std::stringstream();
                        ss->str(textureIndice);
                        *ss>>f;
                        coorTextures.push_back(f);
                        ss =  new std::stringstream();
                        ss->str(normalIndicestr);
                        *ss>>f;
                        normalIndice.push_back(f);
                    }
                    else
                    {
                        normalIndicestr=lineContent[i+j].substr(pos+2, lineContent[i+j].size());

                        std::stringstream * ss= new std::stringstream();
                        unsigned int f;
                        ss->str(vertexIndice);
                        *ss>>f;
                        indices.push_back(f);

                        ss =  new std::stringstream();
                        ss->str(normalIndicestr);
                        *ss>>f;

                        normalIndice.push_back(f);
                    }
                }
            }
            else
            {
                for(int j=1;j<4;j++)
                {
                    std::stringstream ss;
                    unsigned int f;
                    ss.str(lineContent[i+j]);
                    ss >> f;

                    indices.push_back(f);
                }
            }
            i+=3;
        }

    }


    float fVertices[indices.size()*3];
    float fColor[indices.size()*3];
    float fNormals[indices.size()*3];



    for(int i=0;i<indices.size();i++)
    {
        fVertices[i*3]=svertices[(indices[i]-1)*3];
        fVertices[i*3+1]=svertices[(indices[i]-1)*3+1];
        fVertices[i*3+2]=svertices[(indices[i]-1)*3+2];

        if(normals.size()>0)
        {
            fNormals[i*3]=  normals[(normalIndice[i]-1)*3];
            fNormals[i*3+1]=normals[(normalIndice[i]-1)*3+1];
            fNormals[i*3+2]=normals[(normalIndice[i]-1)*3+2];
        }
    }

    for(int i=0;i<indices.size()*3;i++)
    {
        fColor[i]=1.0;
    }


    size=indices.size();


    glBindBuffer(GL_ARRAY_BUFFER,vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*indices.size()*3, fVertices, GL_STREAM_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*indices.size()*3, fColor, GL_STREAM_DRAW);

    if(normals.size()>0)
    {
        glBindBuffer(GL_ARRAY_BUFFER, normalBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float)*indices.size()*3, fNormals, GL_STREAM_DRAW);
        normalActivated=1;
    }

    printf("Loaded \n");
    file.close();

}

void Mesh::SetPicking(unsigned char x, unsigned char y, unsigned char z)
{
    picking=1;

    unsigned char * pickColor = new unsigned char[size*3];

    for(int i=0;i<size;i+=3)
    {
        pickColor[i]=x;
        pickColor[i+1]=y;
        pickColor[i+2]=z;
    }

    glBindBuffer(GL_ARRAY_BUFFER,pickingBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*size*3, pickColor, GL_STREAM_DRAW);
}

void Mesh::Transformations()
{
    glTranslatef(translation->x(),translation->y(),translation->z());
    glRotatef(rotation->y(),0,1,0);
    glRotatef(rotation->x(),1,0,0);
    glRotatef(rotation->z(),0,0,1);
}
